import type { CAC } from 'cac';

import { extname } from 'node:path';

import { getStagedFiles } from '@croco/node-utils';

import { circularDepsDetect } from 'circular-dependency-scanner';

// 默认配置
const DEFAULT_CONFIG = {
    allowedExtensions: ['.cjs', '.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue'],
    ignoreDirs: ['dist', '.turbo', 'output', '.cache', 'scripts', 'internal', 'packages/effects/request/src/', 'packages/@core/ui-kit/menu-ui/src/', 'packages/@core/ui-kit/popup-ui/src/'],
    threshold: 0, // 循环依赖的阈值
} as const;

// 类型定义
type CircularDependencyResult = string[];

interface CheckCircularConfig {
    allowedExtensions?: string[];
    ignoreDirs?: string[];
    threshold?: number;
}

interface CommandOptions {
    config?: CheckCircularConfig;
    staged: boolean;
    verbose: boolean;
}

// 缓存机制
const cache = new Map<string, CircularDependencyResult[]>();

/**
 * 格式化循环依赖的输出
 * @param circles - 循环依赖结果
 */
function formatCircles(circles: CircularDependencyResult[]): void {
    if (circles.length === 0) {
        console.log('✅ No circular dependencies found');
        return;
    }

    console.log('⚠️ Circular dependencies found:');
    circles.forEach((circle, index) => {
        console.log(`\nCircular dependency #${index + 1}:`);
        circle.forEach((file) => console.log(`  → ${file}`));
    });
}

/**
 * 检查项目中的循环依赖
 * @param options - 检查选项
 * @param options.staged - 是否只检查暂存区文件
 * @param options.verbose - 是否显示详细信息
 * @param options.config - 自定义配置
 * @returns Promise<void>
 */
async function checkCircular({ config = {}, staged, verbose }: CommandOptions): Promise<void> {
    try {
        // 合并配置
        const finalConfig = {
            ...DEFAULT_CONFIG,
            ...config,
        };

        // 生成忽略模式
        const ignorePattern = `**/{${finalConfig.ignoreDirs.join(',')}}/**`;

        // 检查缓存
        const cacheKey = `${staged}-${process.cwd()}-${ignorePattern}`;
        if (cache.has(cacheKey)) {
            const cachedResults = cache.get(cacheKey);
            if (cachedResults) {
                verbose && formatCircles(cachedResults);
            }
            return;
        }

        // 检测循环依赖
        const results = await circularDepsDetect({
            absolute: staged,
            cwd: process.cwd(),
            ignore: [ignorePattern],
        });

        if (staged) {
            let files = await getStagedFiles();
            const allowedExtensions = new Set(finalConfig.allowedExtensions);

            // 过滤文件列表
            files = files.filter((file) => allowedExtensions.has(extname(file)));

            const circularFiles: CircularDependencyResult[] = [];

            for (const file of files) {
                for (const result of results) {
                    const resultFiles = result.flat();
                    if (resultFiles.includes(file)) {
                        circularFiles.push(result);
                    }
                }
            }

            // 更新缓存
            cache.set(cacheKey, circularFiles);
            verbose && formatCircles(circularFiles);
        } else {
            // 更新缓存
            cache.set(cacheKey, results);
            verbose && formatCircles(results);
        }

        // 如果发现循环依赖，只输出警告信息
        if (results.length > 0) {
            console.log('\n⚠️ Warning: Circular dependencies found, please check and fix');
        }
    } catch (error) {
        console.error('❌ Error checking circular dependencies:', error instanceof Error ? error.message : error);
    }
}

/**
 * 定义检查循环依赖的命令
 * @param cac - CAC实例
 */
function defineCheckCircularCommand(cac: CAC): void {
    cac.command('check-circular')
        .option('--staged', 'Only check staged files')
        .option('--verbose', 'Show detailed information')
        .option('--threshold <number>', 'Threshold for circular dependencies', {
            default: 0,
        })
        .option('--ignore-dirs <dirs>', 'Directories to ignore, comma separated')
        .usage('Analyze project circular dependencies')
        .action(async ({ ignoreDirs, staged, threshold, verbose }) => {
            const config: CheckCircularConfig = {
                threshold: Number(threshold),
                ...(ignoreDirs && { ignoreDirs: ignoreDirs.split(',') }),
            };

            await checkCircular({
                config,
                staged,
                verbose: verbose ?? true,
            });
        });
}

export { type CheckCircularConfig, defineCheckCircularCommand };
